// 记录传入的Vue 构造函数
let _Vue = null
export default class VueRouter {
  // 实现Vue插件机制 Vue.use(VueRouter)时执行
  static install (Vue) {
    // 1. 判断当前插件是否被安装  用于是否需要安装
    if (VueRouter.install.installed) {
      return
    }
    VueRouter.install.installed = true
    // 2. 把Vue构造函数记录到全局变量 用于使用Vue构造函数的相关方法
    _Vue = Vue
    // 3. 创建Vue实例时传入的router对象注入到所有的vue实例中  使用this.$router
    // _Vue.prototype.$router = this.$options.router // this指向VueRouter 应该使this指向vue实例
    // 使用混入获取vue实例 所有Vue实例混入选项
    _Vue.mixin({
      // beforeCreate钩子，实例创建前执行，添加router对象
      beforeCreate () {
        // Vue实例执行  Vue组件不执行 避免重复执行
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
          this.$options.router.init()
        }
      }
    })
  }

  // Vue Router 构造函数 返回Vue Router
  // 初始化vue router 三个属性
  constructor (options) {
    // vue router 规则
    this.options = options
    // 将规则以键值对的方式存储 键为路由地址 值为路由组件
    // 根据键值对查找路由组件进行渲染
    this.routeMap = {}

    // 响应式对象 current用于记录当前路由地址 hash模式 默认为‘#/’根目录
    // Vue.observable()将传入对象转换为响应式对象
    this.data = _Vue.observable({
      // 刷新页面保持当前路由页面
      current: (window.location.hash && window.location.hash.substr(1)) || '/'
    })

    // 将初始页面的路由更改为hash模式  保持刷新地址栏不变回主页
    if (options.mode !== 'history') {
      window.location.hash = '#' + this.data.current
    }
  }

  init () {
    this.createRouterMap()
    this.initComponents(_Vue)
    this.initEvent()
  }

  // 将options路由规则转换为键值对存储
  createRouterMap () {
    // 遍历所有路由规则，把路由规则解析成键值对的形式存储到routeMap中
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component
    })
  }

  // 创建两个组件 router-link与router-view
  initComponents (Vue) {
    // 创建router-link 接收路由地址 并且本质为超链接
    // 此处超链接跳转会向服务器发送请求 单页面不需要发送服务器请求
    // 因此拦截超链接跳转并修改地址栏中的地址 重新给window.hash赋值 并且将路由地址记录到data中current中
    Vue.component('router-link', {
      props: {
        to: String
      },
      // 使用插槽，在使用时会将内容填充到插槽中
      // 运行时版本 不支持template
      // template: '<a :href="to"><slot></slot></a>'
      render (h) {
        // h函数传入三个参数 第一个为创建标签 第二个为标签属性或事件 第三个参数为为数组 是生成元素的子元素
        return h('a', {
          // 标签属性
          attrs: {
            href: this.to
          },
          // 注册点击事件
          on: {
            click: this.clickHandler
          }
          // 获取默认插槽的值 未起名
        }, [this.$slots.default])
      },
      // 事件处理的方法
      methods: {
        clickHandler (e) {
          // 改变地址栏中的地址  不发送服务器请求
          window.location.hash = '#' + this.to
          // 将地址记录到当前路由数据中
          this.$router.data.current = this.to
          // 阻止事件的默认行为
          e.preventDefault()
        }
      }
    })

    const self = this
    Vue.component('router-view', {
      render (h) {
        // 查找当前路由所对应的组件
        let component = self.routeMap[self.data.current]
        // 若没找到则查找404组件
        if (!component) {
          component = self.routeMap['*']
        }
        // 渲染组件
        return h(component)
      }
    })
  }

  // 监听地址栏变化 处理前进和后退没有加载相应组件的问题
  initEvent () {
    window.addEventListener('hashchange', () => {
      // 此处this指向vuerouter
      // 将data中当前路由指向更改
      const hash = window.location.hash
      this.data.current = hash.substr(1)
    })
  }
}
